########################################################################################
##  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING THIS SOFTWARE
##
##  By downloading, copying, installing or using the software you agree to this license.
##  If you do not agree to this license, do not download, install, copy or use the software.
##
## Copyright (C) 2013, AerVision Technologies Pty Ltd, all rights reserved.
##
## Redistribution and use in source and binary forms, with or without modification,
## are permitted provided that the following conditions are met:
##
##   * Redistribution's of source code must retain the above copyright notice,
##     this list of conditions and the following disclaimer.
##
##   * Redistribution's in binary form must reproduce the above copyright notice,
##     this list of conditions and the following disclaimer in the documentation
##     and/or other materials provided with the distribution.
##
##   * The name of AerVision Technologies, may not be used to endorse or promote products
##     derived from this software without specific prior written permission.
##
## This software is provided by the copyright holders and contributors to demonstrate 
## www.AerCloud.net Framework as a Service API (FaaS), "as is" and
## any express or implied warranties, including, but not limited to, the implied
## warranties of merchantability and fitness for a particular purpose are disclaimed.
## In no event shall the copyright holder or contributors be liable for any direct,
## indirect, incidental, special, exemplary, or consequential damages
## (including, but not limited to, procurement of substitute goods or services;
## loss of use, data, or profits; or business interruption) however caused
## and on any theory of liability, whether in contract, strict liability,
## or tort (including negligence or otherwise) arising in any way out of
## the use of this software, even if advised of the possibility of such damage.
########################################################################################

from logging import Handler

from Tkinter import Frame, Scrollbar, Canvas, Label, Listbox, Pack, Grid, Place
from PIL import Image, ImageTk
 
class AutoWrapLabel(Label):
    def __init__(self, master=None, cnf={}, **kw):
        Label.__init__(self, master, cnf, **kw)
        self.bind("<Configure>", self.resize)
    
    def get_padx(self):
        pad = int(str(self['bd']))
        pad += int(str(self['padx']))
        pad *= 2
        return pad
        
    def resize(self, event):
        w = self.master.winfo_width() - self.get_padx()
        self.configure(wraplength = w)

class ScrolledFrame(Frame):
    """A pure Tkinter scrollable frame that actually works!

    * Use the 'interior' attribute to place widgets inside the scrollable frame
    * Construct and pack/place/grid normally
    * This frame only allows vertical scrolling
    
    """
    def __init__(self, parent, *args, **kw):
        Frame.__init__(self, parent, *args, **kw)            
        # create a canvas object and a vertical scrollbar for scrolling it
        vscrollbar = Scrollbar(self, orient='vertical')
        vscrollbar.pack(fill='y', side='right', expand=0)
        hscrollbar = Scrollbar(self, orient='horizontal')
        hscrollbar.pack(fill='x', side='bottom', expand=0)
        canvas = Canvas(self, bd=0, highlightthickness=0,
                        yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set)
        canvas.pack(side='left', fill='both', expand=1)
        vscrollbar.config(command=canvas.yview)
        hscrollbar.config(command=canvas.xview)

        # reset the view
        canvas.xview_moveto(0)
        canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = interior = Frame(canvas)
        interior_id = canvas.create_window(0, 0, window=interior,
                                           anchor='nw')

        # track changes to the canvas and frame width and sync them,
        # also updating the scrollbar
        def _configure_interior(event):
            # update the scrollbars to match the size of the inner frame
            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
            canvas.config(scrollregion="0 0 %s %s" % size)
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the canvas's width to fit the inner frame
                canvas.config(width=interior.winfo_reqwidth())
            if interior.winfo_reqheight() != canvas.winfo_height():
                # update the canvas's width to fit the inner frame
                canvas.config(height=interior.winfo_reqheight())
        interior.bind('<Configure>', _configure_interior)

        def _configure_canvas(event):
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the inner frame's width to fill the canvas
                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
        #canvas.bind('<Configure>', _configure_canvas)

        return

class ScrolledListbox(Listbox):
    """ Listbox with vertical scroll bar """
    
    def __init__(self, master, **key):
        self.frame = Frame(master)
        self.yscroll = Scrollbar (self.frame, orient='vertical')
        self.yscroll.pack(side='right', fill='y', expand=1)
        key['yscrollcommand']=self.yscroll.set
        Listbox.__init__(self, self.frame, **key)
        self.pack(side='left', fill='both', expand=1)
        self.yscroll.config(command=self.yview)

        # Copy geometry methods of self.frame 
        for m in (Pack.__dict__.keys() + Grid.__dict__.keys() +Place.__dict__.keys()):
            m[0] == '_' or m == 'config' or m == 'configure' or \
                setattr(self, m, getattr(self.frame, m))

class WidgetLogger(Handler):
    def __init__(self, widget):
        Handler.__init__(self)
        self.widget = widget

    def append_log(self, record):
        self.widget.configure(state='normal')
        self.widget.insert('insert', self.format(record) + '\n')
        self.widget.see("end")
        self.widget.configure(state='disabled')
        
    def emit(self, record):
        # Append message (record) to the widget
        self.widget.after(0, lambda: self.append_log(record))
        
class AutoSizePhotoFrame(Frame):
    def __init__(self, parent, *args, **kw):
        self.file = kw.pop("file", None)
        self.label = kw.pop("label", None)
        Frame.__init__(self, parent, *args, **kw)
        if self.file != None:
            self.original = Image.open(self.file)
        else:
            self.original = None
        self.display = Canvas(self, bd=0, highlightthickness=0)
        self.display.bind("<Configure>", self.resize)
        self.display.pack(fill='both', expand=1)
        
    def resize(self, event):
        if self.original == None:
            return
        (w, h) = self.original.size
        ew = event.width
        eh = event.height
        r = min(ew*1.0/w, eh*1.0/h)
        size = (int(r*w), int(r*h))
        resized = self.original.resize(size, Image.ANTIALIAS)
        self.image = ImageTk.PhotoImage(resized)
        self.display.delete("IMG")
        self.display.delete("LBL")
        self.display.create_image(0, 0, image=self.image, anchor='nw', tags="IMG")
        t = self.display.create_text(0, size[1] - 15, text=self.label, anchor='nw', tags="LBL")
        r = self.display.create_rectangle(self.display.bbox(t),fill="yellow", stipple="gray50", width=0)
        self.display.tag_lower(r, t)
        #self.lblCurrentImage = Label(self, text=self.imagename, background="yellow", borderwidth=2).grid(row=1, column=1, columnspan=1, sticky='ws')
